package study.week05.gateway.config.aop;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import study.week05.gateway.filter.CustomHttpFilter;
import study.week05.gateway.netty.ServerContainer;
import study.week05.gateway.router.CustomRouter;

import java.nio.charset.StandardCharsets;
import java.util.List;

import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

@Aspect
@Component
public class FilterRouteAop {
    @Autowired
    private ServerContainer serverContainer;

    @Autowired
    CustomRouter router;

    @Autowired
    private CustomHttpFilter[] filterChain ;

    /**
     * 入的切入点
     */
    @Pointcut("execution(public * study.week05.gateway.inbound.InboundHandler.channelRead(..))")
    public void inboundPoint(){}

    /**
     * 出的切入点
     */
    @Pointcut("execution(public * study.week05.gateway.outbound.JmsHandler.receiveResponse(..))")
    public void outboundPoint(){}
    /**
     * 路由的切入点
     */
    @Pointcut("execution(public * study.week05.gateway.outbound.JmsHandler.sendRequest(..))")
    public void routePoint(){}

    @Around("inboundPoint()")
    public Object doAroundInboundPoint(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        try {
            Object[] args = proceedingJoinPoint.getArgs();
            ChannelHandlerContext ctx= (ChannelHandlerContext) args[0];
            FullHttpRequest fullRequest = (FullHttpRequest) args[1];
            for (int i = 0; i < filterChain.length; i++) {
                if(!filterChain[i].inbound(fullRequest,ctx)){
                    //返回false就结束请求
                    inboundRefuse(fullRequest,ctx);
                    return null;
                }
            }

            Object obj = proceedingJoinPoint.proceed();
            return obj;
        } catch (Throwable throwable) {
            throw throwable;
        }
    }


    /**
     * 过滤器链拒绝请求时的统一处理
     * @param fullRequest
     * @param ctx
     */
    public void inboundRefuse(FullHttpRequest fullRequest, ChannelHandlerContext ctx) {
        FullHttpResponse response = null;
        try {
            String body = "inbound filter refuse.";

            response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(body.getBytes(StandardCharsets.UTF_8)));
            response.headers().set("Content-Type", "application/json");
            response.headers().setInt("Content-Length", response.content().readableBytes());

        } catch (Exception e) {
            System.out.println("处理出错:"+e.getMessage());
            response = new DefaultFullHttpResponse(HTTP_1_1, NO_CONTENT);
        } finally {
            if (fullRequest != null) {
                if (!HttpUtil.isKeepAlive(fullRequest)) {
                    ctx.write(response).addListener(ChannelFutureListener.CLOSE);
                } else {
                    response.headers().set(CONNECTION, KEEP_ALIVE);
                    ctx.write(response);
                }
                ctx.flush();
            }
        }
    }

    @Around("outboundPoint()")
    public Object doAroundOutboundPoint(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        try {
            //
            Object[] args = proceedingJoinPoint.getArgs();
            FullHttpResponse response = (FullHttpResponse) proceedingJoinPoint.proceed();
            FullHttpRequest fullRequest = (FullHttpRequest) args[0];
            ChannelHandlerContext ctx= (ChannelHandlerContext) args[1];
            for (int i = filterChain.length -1; i >=0 ; i--) {
                if(!filterChain[i].outbound(response)){
                    //过滤器返回false就结束请求
                    outboundRefuse(fullRequest,ctx);
                    return null;
                }
            }
            return response;
        } catch (Throwable throwable) {
            throw throwable;

        }
    }


    /**
     * 过滤器链拒绝请求时的统一处理
     * @param fullRequest
     * @param ctx
     */
    private void outboundRefuse(FullHttpRequest fullRequest, ChannelHandlerContext ctx) {
        FullHttpResponse response = null;
        try {
            String body = "outbound filter refuse.";

            response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(body.getBytes(StandardCharsets.UTF_8)));
            response.headers().set("Content-Type", "application/json");
            response.headers().setInt("Content-Length", response.content().readableBytes());

        } catch (Exception e) {
            System.out.println("处理出错:"+e.getMessage());
            response = new DefaultFullHttpResponse(HTTP_1_1, NO_CONTENT);
        } finally {
            if (fullRequest != null) {
                if (!HttpUtil.isKeepAlive(fullRequest)) {
                    ctx.write(response).addListener(ChannelFutureListener.CLOSE);
                } else {
                    response.headers().set(CONNECTION, KEEP_ALIVE);
                    ctx.write(response);
                }
                ctx.flush();
            }
        }
    }

    @Around("routePoint()")
    public Object doAroundRoutePoint(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        try {
            Object[] args = proceedingJoinPoint.getArgs();
            String server = router.route(serverContainer.getServerList());
            args[2] = server;
            return proceedingJoinPoint.proceed(args);
        } catch (Throwable throwable) {
            throw throwable;

        }
    }
}
